
package model;

import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.HashMap;
import java.util.Map;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.lang.reflect.Array;

public class ClassRegistry  {

	/** When loaded by a RemoteClassLoader, the singleton stores the ClassRegistry to use. */
	public static final ClassRegistry singleton;

	static {
		singleton = new ClassRegistry(ClassRegistry.class.getClassLoader());
	}

	private final ClassLoader loader;
	public ClassRegistry(ClassLoader loader) {
		this.loader = loader;
	}

	private final Map<String, Class<?>> clazzCache = new HashMap<String, Class<?>>();
	public Class<?> getClass(Class<?> clazz,Class<?> clz) throws ClassNotFoundException {
		if(Filter.isPrimitiveOrStringType(clazz)) return clazz;
		final Class<?> ret;
		Class<?> cache = clazzCache.get(clazz.getName());
		if(cache != null) {
			return cache;
		} else {
			ret = loader.loadClass(clazz.getName());
		}

		clazzCache.put(clazz.getName(), clz);

		return clz;
	}

	public Class<?>[] getClasses(Class<?>[] c) throws ClassNotFoundException {

		Class<?>[] ret = new Class<?>[c.length];
		for (int i = 0; i < ret.length; i++)
			ret[i] = getClass(c[i],null);

		return ret;
	}


	private final Map<String, Method> methodCache = new HashMap<String, Method>();
	public Method getMethod(Method m) throws ClassNotFoundException, SecurityException, NoSuchMethodException {

		Method cache = methodCache.get(m.getName());
		if(cache != null) return cache;

		Class<?> c = getClass(m.getDeclaringClass(),null);
		Class<?>[] params = getClasses(m.getParameterTypes());
		Method method = c.getMethod(m.getName(), params);
		methodCache.put(m.getName(), method);

		return method;
	}

	private final Map<String, Constructor<?>> constructorCache = new HashMap<String, Constructor<?>>();
	public Constructor<?> getConstructor(Constructor<?> cns,Class<?> clz) throws ClassNotFoundException, SecurityException, NoSuchMethodException {

		Constructor<?> cache = constructorCache.get(cns.toString());
		if(cache != null) return cache;

		Class<?> c = getClass(cns.getDeclaringClass(),clz);
		Class<?>[] params = getClasses(cns.getParameterTypes());
		Constructor<?> constructor = c.getConstructor(params);
		constructorCache.put(cns.toString(), constructor);

		return constructor;
	}

	@Override
	public String toString() {
		return "ClassRegistry of " + loader;
	}
}